﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;

namespace WPFBehaviorLibrary.DragDropBehavior
{
	/// <summary>
	/// Handles the visual display of the item as it's being dragged
	/// </summary>
	public class DragAdorner
		: Adorner, IDisposable
	{
		private ContentPresenter myContentPresenter;
		private AdornerLayer myAdornerLayer;
		private double myLeftOffset;
		private double myTopOffset;

		/// <summary>
		/// Initializes a new instance of the <see cref="DragAdorner"/> class.
		/// </summary>
		/// <param name="data">The data that's being dragged.</param>
		/// <param name="dataTemplate">The data template to show while dragging.</param>
		/// <param name="adornedElement">The adorned element.</param>
		/// <param name="adornerLayer">The adorner layer.</param>
		public DragAdorner(object data, DataTemplate dataTemplate, UIElement adornedElement, AdornerLayer adornerLayer)
			: base(adornedElement)
		{
			if (data == null) throw new ArgumentNullException("data");
			if (adornerLayer == null) throw new ArgumentNullException("adornerLayer");

			myAdornerLayer = adornerLayer;
			myContentPresenter = new ContentPresenter() { Content = data, ContentTemplate = dataTemplate, Opacity = 0.75 };

			myAdornerLayer.Add(this);
		}

		/// <summary>
		/// Implements any custom measuring behavior for the adorner.
		/// </summary>
		/// <param name="constraint">A size to constrain the adorner to.</param>
		/// <returns>
		/// A <see cref="T:System.Windows.Size"/> object representing the amount of layout space needed by the adorner.
		/// </returns>
		protected override Size MeasureOverride(Size constraint)
		{
			myContentPresenter.Measure(constraint);
			return myContentPresenter.DesiredSize;
		}

		/// <summary>
		/// When overridden in a derived class, positions child elements and determines a size for a <see cref="T:System.Windows.FrameworkElement"/> derived class.
		/// </summary>
		/// <param name="finalSize">The final area within the parent that this element should use to arrange itself and its children.</param>
		/// <returns>The actual size used.</returns>
		protected override Size ArrangeOverride(Size finalSize)
		{
			myContentPresenter.Arrange(new Rect(finalSize));
			return finalSize;
		}

		/// <summary>
		/// Overrides <see cref="M:System.Windows.Media.Visual.GetVisualChild(System.Int32)"/>, and returns a child at the specified index from a collection of child elements.
		/// </summary>
		/// <param name="index">The zero-based index of the requested child element in the collection.</param>
		/// <returns>
		/// The requested child element. This should not return null; if the provided index is out of range, an exception is thrown.
		/// </returns>
		protected override Visual GetVisualChild(int index)
		{
			return myContentPresenter;
		}

		/// <summary>
		/// Gets the number of visual child elements within this element.
		/// </summary>
		/// <value></value>
		/// <returns>
		/// The number of visual child elements for this element.
		/// </returns>
		protected override int VisualChildrenCount
		{
			get { return 1; }
		}

		/// <summary>
		/// Updates the position of the adorner relative to the adorner layer.
		/// </summary>
		/// <param name="left">The offset from the left.</param>
		/// <param name="top">The offset from the top.</param>
		public void UpdatePosition(double left, double top)
		{
			myLeftOffset = left;
			myTopOffset = top;

			if (myAdornerLayer != null)
			{
				myAdornerLayer.Update(this.AdornedElement);
			}
		}

		/// <summary>
		/// Returns a <see cref="T:System.Windows.Media.Transform"/> for the adorner, based on the transform that is currently applied to the adorned element.
		/// </summary>
		/// <param name="transform">The transform that is currently applied to the adorned element.</param>
		/// <returns>A transform to apply to the adorner.</returns>
		public override GeneralTransform GetDesiredTransform(GeneralTransform transform)
        {
            GeneralTransformGroup result = new GeneralTransformGroup();
            result.Children.Add(base.GetDesiredTransform(transform));
            result.Children.Add(new TranslateTransform(myLeftOffset, myTopOffset));
            return result;
        }
		
		#region IDisposable Members

		/// <summary>
		/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
		/// </summary>
		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		/// <summary>
		/// Releases unmanaged and - optionally - managed resources
		/// </summary>
		/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
		protected virtual void Dispose(bool disposing)
		{
			if (disposing)
			{
				//Free managed resources
				myAdornerLayer.Remove(this);
			}
		}

		#endregion

	}
}
